﻿#include "../../ctlSingleEdit.h"
#include "../../edit/clipboard.h"

#define EDIT_CHECK_MARCO \
	((unsigned)getWidth() < getRightPadding() + getLeftPadding())

namespace swui
{
	bool ctlIsBlank(std::wstring::value_type value);
	bool ctlIsAlnum(std::wstring::value_type value);
	void ctlSingleEdit::_flush_font_()
	{
		IFont::_flush_font_();
		_edit_str = getFont()->getTextExtentDistance(_textpack.wptr(), wcslen(_textpack.wptr()), 0xffffffff);
		_flush_caret();
		_check_scroll();
	}
	void ctlSingleEdit::_position_event_(ctlPositionEvent* pe)
	{
		ctlRoot::_position_event_(pe);
		if(pe->eventType == ctlPositionEventType::size)
		{
//			_edit_str = getFont()->getTextExtentDistance(_textpack.wptr(), wcslen(_textpack.wptr()), 0xffffffff);
			_flush_caret();
			_check_scroll();
		}
	}
	void ctlSingleEdit::_key_event_(ctlKeyEvent* ke)
	{
		ctlRoot::_key_event_(ke);
		if(ke->eventType == ctlKeyEventType::input && accessWrite()) {
			//判断字符是否可打印
			std::wstring::value_type text[2] = { ke->charInfo.wtext, 0 };
			if(!_eventTrans.getMajorInfo()->isUnicode)
				MultiByteToWideChar(CP_ACP, 0, ke->charInfo.text, -1, text, 1);
			if(!(*text < 0x20 && *text != '\t' || *text == 0x7f))
			{
				//插入
				ctlNotifyEvent ne;
				ctlNotifyEdit nee;
				ne.eventType = ctlNotifyEventType::edit;
				ne.info = &nee;
				nee.state = ctlNotifyEdit::insert;
				bool allow = true;
				nee.isAllow = &allow;
				nee.str = nullptr;
				if(_eventTrans.getMajorInfo()->isUnicode)
					nee.text.wc = ke->charInfo.wtext;
				else memcpy(nee.text.c, ke->charInfo.text, 3);
				_notify_event_(&ne);
				if(allow)
				{
					_remove_selected();
					_ctlInterior::textpack pack;
					pack.set(text);
					_edit_caret_pos_begin = _edit_caret_pos_end = 
						_insert_text(_edit_caret_pos_begin, pack);
					_send_change_event();
					_check_scroll();
					_flush_caret();
					redraw();
				}
			}
		} else if(ke->eventType == ctlKeyEventType::down) {
			if(GetKeyState(VK_CONTROL) & 0xff00) {
				decltype(_edit_back_insert) tempBack = _edit_back_insert;
				switch(ke->charInfo.virtualKey)
				{
				case 'Z':
					{
						decltype(_edit_back_insert) tempBack = _edit_back_insert;
						_edit_back_insert.enable = false;
						if(_edit_back_remove.enable && accessWrite())
						{
							_edit_caret_pos_begin = _edit_back_remove.begin;
							_edit_caret_pos_end = _edit_back_remove.end;
							_remove_selected();
						}
						if(tempBack.enable && accessWrite())
						{
							_exactPos begin = tempBack.begin;
							_edit_caret_pos_end = _insert_text(tempBack.begin, tempBack.pack);
							_edit_caret_pos_begin = begin;
						}
					}
					_send_change_event();			
					_check_scroll();
					_flush_caret();
					redraw();
					break;
				case VK_RIGHT:
					{
						_exactPos eps = _edit_caret_pos_end;
						unsigned textsize = getTextLength();
						//如果插入符在末尾
						if(eps == textsize)
						{
							break;
						} else {
							//忽略空白符
							auto begin = _textpack.stdwstr().begin(), 
								end = _textpack.stdwstr().end(), 
								iter = begin + eps;
							std::wstring::value_type begin_char = *iter;
							while(iter != end && ctlIsBlank(*iter)) ++iter;
							//查找连续字符
							while(iter != end && ctlIsAlnum(*iter)) ++iter;
							_edit_caret_pos_end = iter - begin;
							if(!ctlIsAlnum(begin_char) && !ctlIsAlnum(begin_char))
								_edit_caret_pos_end = ctlMin(textsize, _edit_caret_pos_end + 1);
						}
					}
					if((GetKeyState(VK_SHIFT) & 0xff00) == 0 || !accessRead())
						_edit_caret_pos_begin = _edit_caret_pos_end;
					_check_scroll();
					_flush_caret();
					redraw();
					break;
				case VK_LEFT:
					{
						_exactPos eps = _edit_caret_pos_end;
						//如果插入符在行开头
						if(eps == 0)
						{
							break;
						} else {
							//忽略空白符
							auto begin = _textpack.stdwstr().begin(),
								iter = begin + eps - 1;
							int begin_char = static_cast<int>(*iter);
							while(iter != begin && ctlIsBlank(*iter)) --iter;
							//查找连续字符
							while(iter != begin && ctlIsAlnum(*iter)) --iter;
							_edit_caret_pos_end = iter - begin == 0 ? 0 : iter - begin + 1;
							if(!ctlIsBlank(begin_char) && !ctlIsAlnum(begin_char)
								&& _edit_caret_pos_end != 0)
								--_edit_caret_pos_end;
						}
					}
					if((GetKeyState(VK_SHIFT) & 0xff00) == 0 || !accessRead())
						_edit_caret_pos_begin = _edit_caret_pos_end;
					_check_scroll();
					_flush_caret();
					redraw();
					break;
				case 'A':
					if(accessRead())
					{
						_edit_caret_pos_begin = 0;
						_edit_caret_pos_end = getTextLength();
						_check_scroll();
						_flush_caret();
						redraw();
					}
					break;
				case 'C':
					if(_edit_caret_pos_begin != _edit_caret_pos_end && accessRead())
					{
						std::wstring tempstr = getSelectedWideString();
						if(_send_edit_event(tempstr, ctlNotifyEdit::copy))
							clipboard_copy(tempstr);
					}
					break;
				case 'X':
					if(_edit_caret_pos_begin != _edit_caret_pos_end 
						&& accessRead() && accessWrite())
					{
						std::wstring tempstr = getSelectedWideString();
						if(_send_edit_event(tempstr, ctlNotifyEdit::cut))
						{
							clipboard_copy(tempstr);
							_remove_selected();
							_send_change_event();
							_check_scroll();
							_flush_caret();
							redraw();
						}
					}
					break;
				case 'V':
					if(accessWrite())
					{
						_ctlInterior::textpack pack;
						clipboard_get(pack);
						if(pack.stdwstr().empty() == false &&
							_send_edit_event(pack.stdwstr(), ctlNotifyEdit::paste))
						{
							_remove_selected();
							_exactPos eps = _insert_text(_edit_caret_pos_begin, pack);
							_edit_caret_pos_begin = _edit_caret_pos_end = eps;
							_send_change_event();
							_check_scroll();
							_flush_caret();
							redraw();
						}
					}
					break;
				}
			} else {
				bool eventFirst = false;
				switch(ke->charInfo.virtualKey)
				{
				case VK_LEFT:
				case VK_BACK:
					eventFirst = true;
				case VK_RIGHT:
				case VK_DELETE:
					{
						bool leftRight = ke->charInfo.virtualKey == VK_LEFT ||
							ke->charInfo.virtualKey == VK_RIGHT;
						bool shift = (GetKeyState(VK_SHIFT) & 0xff00) != 0;
						if(_edit_caret_pos_end == _edit_caret_pos_begin || shift)
						{
							if(_edit_caret_pos_end != 0 && eventFirst)
								--_edit_caret_pos_end; 	
							else if(_edit_caret_pos_end != getTextLength() && !eventFirst)
								++_edit_caret_pos_end;
						}
						if(!accessRead() && shift)
							_edit_caret_pos_end = _edit_caret_pos_begin;
						if(leftRight)
						{
							if(ke->charInfo.virtualKey == VK_LEFT && !shift)
								_edit_caret_pos_begin = _edit_caret_pos_end
								= ctlMin(_edit_caret_pos_begin, _edit_caret_pos_end);
							else if(ke->charInfo.virtualKey == VK_RIGHT && !shift)
								_edit_caret_pos_begin = _edit_caret_pos_end
								= ctlMax(_edit_caret_pos_begin, _edit_caret_pos_end);
						} else if(accessWrite()) {
							_remove_selected();
							_send_change_event();
						}
						_check_scroll();
						_flush_caret();
						redraw();
					}
					break;
				}
			}
		}
	}
	void ctlSingleEdit::_mouse_event_(ctlMouseEvent* me)
	{
		if(me->eventModel != ctlMouseEventModel::cut) return;
		ctlRoot::_mouse_event_(me);
		if(me->eventType == ctlMouseEventType::move && getAccess() != ctlAccess::none)
			_eventTrans.setCursor(reinterpret_cast<std::wstring::const_pointer>(IDC_IBEAM));
		if(me->eventType == ctlMouseEventType::leftDown)
		{
			_exactPos pos = _edit_map_point_to_pos(me->mousePos);
			_edit_caret_pos_begin = _edit_caret_pos_end = pos;
			_check_scroll();
			_flush_caret();
			_capture_mouse_move();
		} else if(me->eventType == ctlMouseEventType::move && isMouseDown() && accessRead()) {
			_edit_caret_pos_end = _edit_map_point_to_pos(me->mousePos);
			_check_scroll();
			_flush_caret();
			redraw();
		} else if(me->eventType == ctlMouseEventType::leftUp) {
			_release_mouse_move();
			if(isPointInControl(me->mousePos) == false)
				_eventTrans.releaseCursor();
		} else if(me->eventType == ctlMouseEventType::leave) {
			_eventTrans.releaseCursor();
		}
	}
	bool ctlSingleEdit::create(ctlRoot* parent, ctlPoint pos, unsigned width, 
			const ctlText& lpszText, const ctlStyleEdit& style)
	{
		if(parent == 0 || isCreated() || !parent->isCreated()) return false;
		std::lock_guard<decltype(_eventTrans)> gurad(
			_eventTrans.getControlEventTrans(parent));
		_control_virtual_class_end = this;
		_basic_create_(ctlRect(pos.x, pos.y, width, 100), parent);
		_controlType = ctlControlType::ctlSingleEdit;
		_virtual_font_create();
		lockDrawing();

		_rect.cy = getFont()->getFontHeight() + getHeightPadding() * 2;
		enableBackground(true);
		_edit_caret_pos_begin = _edit_caret_pos_end = 0;
		_edit_left_offset = _edit_left_pos = 0;
		_edit_access = ctlAccess::readWrite;
		
		setStyle(style);
		setText(lpszText);
		setTextAlignMode(textAlign::left);

		unlockDrawing();
		redraw();
		return true;
	}
	bool ctlSingleEdit::setText(const ctlText& lpszText)
	{
		if(!isCreated() || !isEnabled()) return false;
		ctlAccess oldAccess = getAccess();
		setAccess(ctlAccess::write);
		_edit_str.clear();
		_textpack.stdwstr().clear();
		_ctlInterior::textpack pack;
		pack.set(lpszText);
		_edit_caret_pos_begin = _edit_caret_pos_end = 0;
		_insert_text(0, pack);
		setAccess(oldAccess);
		_edit_back_insert.enable = false;
		_check_scroll();
		_flush_caret();
		redraw();
		return true;
	}
	ctlSingleEdit::_exactPos ctlSingleEdit::_edit_map_point_to_pos(ctlPoint pt)
	{
		if(_edit_str.empty() || EDIT_CHECK_MARCO) return 0;
		pt.x -= getLeftPadding();
		_exactPos pps = _edit_left_pos;
		auto iter = _edit_str.begin() + pps;
		int pos1 = static_cast<int>(_edit_left_offset) - *iter;
		while(iter != _edit_str.end() && pt.x > pos1 + *iter / 2)
		{
			pos1 += *iter++;
			++pps;
		}
		while(iter != _edit_str.begin() && pt.x < pos1 - *--iter / 2)
		{
			pos1 -= *iter;
			--pps;
		}
		return pps;
	}
	ctlPoint ctlSingleEdit::_edit_map_pos_to_point(_exactPos pos)
	{
		ctlPoint pt(getLeftPadding(), getHeightPadding());
		if(EDIT_CHECK_MARCO) return pt;
		if(_edit_str.empty()) return pt;
		auto iter = _edit_str.begin() + _edit_left_pos;
		pt.x -= *iter - static_cast<int>(_edit_left_offset);
		while(pos > _edit_left_pos)
		{
			pt.x += *iter++;
			--pos;
		}
		while(pos < _edit_left_pos)
		{
			pt.x -= *--iter;
			++pos;
		}
		return pt;
	}
	ctlSingleEdit::_exactPos ctlSingleEdit::_insert_text(_exactPos pos, const _ctlInterior::textpack& text)
	{
		if(accessWrite() == false || EDIT_CHECK_MARCO)
			return pos;
		std::wstring wstr;
		std::wstring::const_pointer tabText = L"    ";
		std::wstring::const_iterator iter = text.stdwstr().begin(), 
			iterEnd = text.stdwstr().end();
		_edit_back_remove.begin = pos;
		while(iter != iterEnd && *iter != L'\n' && *iter != L'\r')
		{
			if(*iter == L'\t') wstr.append(tabText);
			else wstr.push_back(*iter);
			++iter;
		}
		if(wstr.empty() || wstr[0] == 0) return pos;
		_textpack.stdwstr().insert(_textpack.stdwstr().begin() + pos, wstr.begin(), wstr.end());
		std::vector<int> txtwid = getFont()->getTextExtentDistance(wstr.c_str(), wcslen(wstr.c_str()), 0xffffffff);
		_edit_str.insert(_edit_str.begin() + pos, txtwid.begin(), txtwid.end());
		_edit_back_remove.end = pos + txtwid.size();
		_edit_back_remove.enable = true;
		if(pos == _edit_left_pos)
			_edit_left_offset = _edit_str[pos];
		return pos + txtwid.size();
	}
	void ctlSingleEdit::_check_scroll()
	{
		if(_edit_left_pos == 0 && _edit_left_offset == 0 && !_edit_str.empty())
		{
			_edit_left_offset = _edit_str.front();
			return;
		}
		if(_edit_caret_pos_end <= _edit_left_pos)
		{
			_edit_left_pos = _edit_caret_pos_end;
			if(_edit_str.empty()) _edit_left_offset = 0;
			else _edit_left_offset = _edit_str[_edit_left_pos];
			return;
		}

		ctlPoint pt = _edit_map_pos_to_point(_edit_caret_pos_end);
		if(pt.x > getWidth() - static_cast<int>(getRightPadding()))
		{
			int moveLen = pt.x - getWidth() + static_cast<int>(getRightPadding());
			if(moveLen < static_cast<int>(_edit_left_offset))
			{
				_edit_left_offset -= moveLen;
				return;
			}
			moveLen -= _edit_left_offset;
			++_edit_left_pos;
			auto iter = _edit_str.begin() + _edit_left_pos;
			while(moveLen >= *iter)
			{
				moveLen -= *iter++;
				++_edit_left_pos;
			}
			_edit_left_offset = *iter - moveLen;
		}
	}
	void ctlSingleEdit::_flush_caret()
	{
		auto& caret = _eventTrans.getMajorInfo()->hCaret;
		if(getAccess() == ctlAccess::none)
		{
			caret.enable(false);
			return;
		}
		if(_edit_caret_pos_end != _edit_caret_pos_begin)
		{
			caret.enable(false);
		} else {
			caret.setColor(static_cast<ctlStyleEdit*>(_styleBase)->caret);
			caret.setLevel(this);
			caret.setLength(getFont()->getFontHeight());
			caret.enable(true);
			caret.setPos(_edit_map_pos_to_point(_edit_caret_pos_begin));
		}
	}
	bool ctlSingleEdit::_send_edit_event(std::wstring& str, decltype(ctlNotifyEdit::copy) state)
	{
		ctlNotifyEvent ne;
		ne.eventType = ctlNotifyEventType::edit;
		ctlNotifyEdit nee;
		ne.info = &nee;
		bool allow = true;
		nee.isAllow = &allow;
		nee.state = state;
		nee.str = &str;
		nee.text.wc = 0;
		_notify_event_(&ne);
		return allow;
	}
	void ctlSingleEdit::_send_change_event()
	{
		ctlNotifyEvent ne;
		ne.info = nullptr;
		ne.eventType = ctlNotifyEventType::editChange;
		_notify_event_(&ne);
	}
	void ctlSingleEdit::_remove_selected()
	{
		if(accessWrite() == false || EDIT_CHECK_MARCO)
			return;
		unsigned begin = ctlMin(_edit_caret_pos_begin, _edit_caret_pos_end), 
			end = ctlMax(_edit_caret_pos_begin, _edit_caret_pos_end);
		//记录撤销信息
		_edit_back_insert.pack.set(getSelectedWideString());
		_edit_back_insert.begin = begin;
		_edit_back_insert.end = end;
		_edit_back_insert.enable = true;
		_edit_back_remove.enable = false;
		
		_edit_str.erase(_edit_str.begin() + begin, _edit_str.begin() + end);
		_textpack.stdwstr().erase(_textpack.stdwstr().begin() + begin, _textpack.stdwstr().begin() + end);
		_edit_caret_pos_end = _edit_caret_pos_begin = begin;
	}
	std::wstring ctlSingleEdit::getSelectedWideString()
	{
		unsigned begin = ctlMin(_edit_caret_pos_begin, _edit_caret_pos_end), 
			end = ctlMax(_edit_caret_pos_begin, _edit_caret_pos_end);
		return std::wstring(_textpack.wptr() + begin, _textpack.wptr() + end);
	}
	std::string ctlSingleEdit::getSelectedString()
	{
		std::wstring ws = getSelectedWideString();
		unsigned len;
		std::string str(len = WideCharToMultiByte(CP_OEMCP, 0, ws.c_str(), -1, 0, 0, 0, 0), 0);
		WideCharToMultiByte(CP_OEMCP, 0, ws.c_str(), -1, &str[0], len, 0, 0);
		return std::move(str);
	}
	unsigned ctlSingleEdit::getCaretPosition()
	{
		if(getFocus() == this)
			return _edit_caret_pos_begin;
		return 0xffffffff;
	}
	bool ctlSingleEdit::setCaretPosition(unsigned pos)
	{
		if(pos > _edit_str.size())
			return false;
		_edit_caret_pos_begin = _edit_caret_pos_end = pos;
		_flush_caret();
		_check_scroll();
		redraw();
		return true;
	}
	bool ctlSingleEdit::setCaretAppend()
	{
		return setCaretPosition(_edit_str.size());
	}
	std::pair<unsigned, unsigned> ctlSingleEdit::getSelected()
	{
		return std::pair<unsigned, unsigned>(
			_edit_caret_pos_begin, _edit_caret_pos_end);
	}
	bool ctlSingleEdit::setSelected(std::pair<unsigned, unsigned> pos)
	{
		if(pos.first > pos.second) return false;
		if(pos.second > _edit_str.size()) return false;
		_edit_caret_pos_begin = pos.first;
		_edit_caret_pos_end = pos.second;
		_flush_caret();
		_check_scroll();
		redraw();
		return true;
	}
}